package com.flejay.dreamrecruiter.dao.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.apache.commons.io.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import com.flejay.dreamrecruiter.dao.IConfigurationLoaderDao;
import com.flejay.dreamrecruiter.model.SubTag;
import com.flejay.dreamrecruiter.model.Tag;
import com.flejay.dreamrecruiter.service.ILoggingService;
import com.flejay.dreamrecruiter.tools.ConfigReaderTool;
import com.flejay.dreamrecruiter.tools.CustomStringUtils;
import com.flejay.dreamrecruiter.tools.IConstantsUtil;

@Singleton
@Named("fileConfigurationLoaderDao")
public class FileConfigurationLoaderDao implements IConfigurationLoaderDao{

	private String prefix = "^\\s*";
	private String allContaining = "(\\s*((\\.|:)(\\s*(:|–|-|—|/))?)?|(\\d+\\s*(–|-|—)\\d+\\s*(:|.|–|-|—|/)?))";

	@Inject
	@Named("logService")
	private ILoggingService logService;
	
	@Override
	public List<Tag> loadTagConfiguration(final String mode, final String language) {
		String conf = language + "_" + mode;
		StringBuffer configFileName = new StringBuffer()
			.append(ConfigReaderTool.FILE_CONFIGURATION_FOLDER)
			.append(conf)
			.append(ConfigReaderTool.FILE_CONFIGURATION_EXTENSION);
		
		Resource resource = new ClassPathResource(configFileName.toString());
		List<Tag> tagList = new ArrayList<Tag>();
		try {
			String file = IOUtils.toString(resource.getInputStream());
			for(String tagLine : file.split(IConstantsUtil.LINE_SEPARATOR)){
				if (!StringUtils.isEmpty(tagLine)){
					String[] splittedContent = tagLine.split(IConstantsUtil.TABULATION);
					if (splittedContent.length != 3){
						throw new IllegalStateException("configuration file is not well formated");
					}
					Tag extractedTag = extractTagValues(splittedContent,conf);
					tagList.add(extractedTag);
				}
			}
		} catch (IOException e) {
			handleCriticalErrorLoadingConfigurationFile(configFileName.toString());
		}
		return tagList;
	}
	
	@Override
	public Map<String,List<SubTag>> loadSubTagsConfiguration(String conf){
		Map<String,List<SubTag>> tagMap = readSubTagConfigFile(conf);
		for (List<SubTag> subTagList : tagMap.values()){
			for (SubTag subTag : subTagList){
				subTag.setRegexp(readSubTagRegex(conf, subTag.getRegexFileName()));
			}
		}
		return tagMap;
	}
	
	/**
	 * Precompile la liste des regexs 
	 * 
	 * @param conf
	 * @param regexFileName
	 * @return
	 */
	private List<Pattern> readSubTagRegex(String conf, String regexFileName) {
		List<Pattern> res = new ArrayList<Pattern>();
		StringBuffer configFileName = new StringBuffer()
			.append(ConfigReaderTool.REGEX_CONFIGURATION_FOLDER)
			.append(conf).append("/")
			.append(ConfigReaderTool.SUB_REGEX_CONFIGURATION_FOLDER)
			.append("regex/")
			.append(regexFileName);
		
		Resource resource = new ClassPathResource(configFileName.toString());
		// chargement des regexs
		String regexs;
		try {
			regexs = IOUtils.toString(resource.getInputStream());
			for(String regex : regexs.split(IConstantsUtil.LINE_SEPARATOR)){
				if (!StringUtils.isEmpty(regex)){
	    			res.add(Pattern.compile(CustomStringUtils.removeBackLine(regex),Pattern.CASE_INSENSITIVE));
				}
			}
		} catch (IOException e) {
			handleCriticalErrorLoadingConfigurationFile(configFileName.toString());
		}
		return res;
	}

	/**
	 * Charge la configuration pour les sous tags
	 * 
	 * @param conf
	 * @return
	 */
	private Map<String, List<SubTag>> readSubTagConfigFile(String conf) {
		Map<String,List<SubTag>> subTags = new HashMap<String,List<SubTag>>();
		StringBuffer configFileName = new StringBuffer()
			.append(ConfigReaderTool.REGEX_CONFIGURATION_FOLDER)
			.append(conf).append("/")
			.append(ConfigReaderTool.SUB_REGEX_CONFIGURATION_FOLDER)
			.append(ConfigReaderTool.CONFIG_FILE);
	
		Resource resource = new ClassPathResource(configFileName.toString());
		try {
			String currentTag = "";
			for(String tagLine : CustomStringUtils.extractLinesFromResource(resource)){
				if (!StringUtils.isEmpty(tagLine)){
					if (tagLine.startsWith("\t")){
						// subTag
						SubTag subTag = SubTag.getNewInstance();
						String[] splitted = tagLine.split("\t");
						if (splitted.length != 5){
							throw new IllegalStateException("configuration file is not well formated");
						}
						subTag.setLabel(splitted[1]);
						subTag.setRegexFileName(splitted[2]);
						if ("0".equalsIgnoreCase(CustomStringUtils.removeBackLine(splitted[3]))){
							subTag.setAgglomerate(false);
						} else {
							subTag.setAgglomerate(true);
						}
						if ("0".equalsIgnoreCase(CustomStringUtils.removeBackLine(splitted[4]))){
							subTag.setIsolatedStratey(false);
						} else {
							subTag.setIsolatedStratey(true);
						}
						List<SubTag> subTagList = subTags.get(currentTag);
						if (subTagList == null){
							subTagList = new ArrayList<SubTag>();
						}
						subTagList.add(subTag);
						subTags.put(currentTag, subTagList);
					} else {
						// tag
						currentTag = CustomStringUtils.removeBackLine(tagLine);
					}
				}
			}
		} catch (IOException e) {
			handleCriticalErrorLoadingConfigurationFile(configFileName.toString());
		}
		return subTags;
	}

	/**
	 * Extract values to construct a full Tag Object
	 * 
	 * @param label
	 * @param splittedContent
	 * @param configFileName
	 * @return
	 */
	private Tag extractTagValues(final String[] splittedContent, final String configFileName) {
		Tag tag = Tag.getNewInstance();
		tag.setLabel(splittedContent[0]);
		if ("1".equalsIgnoreCase(splittedContent[1])){
			tag.setExtract(true);
		} else {
			tag.setExtract(false);
		}
		String sizeO = splittedContent[2].replaceAll("\\r", "");
		tag.setSizeObjective(sizeO);
		tag.setRegexp(extractRegexs(configFileName, tag.getLabel()));
		return tag;
	}

	/**
	 * Extract Precompiled regexs
	 * 
	 * @param configFileName
	 * @param label
	 * @param regexType
	 * @return
	 */
	private List<Pattern> extractRegexs(final String configFileName, final String label) {
		List<Pattern> precompiledRegex = new ArrayList<Pattern>();
		// classpath du fichier de configuration de regex à charger
		String regexFile = new StringBuffer()
			.append(ConfigReaderTool.REGEX_CONFIGURATION_FOLDER)
			.append(configFileName)
			.append("/")
			.append(label)
			.append(ConfigReaderTool.FILE_CONFIGURATION_EXTENSION)
			.toString();
		
		// chargement des regexs
		Resource resource = new ClassPathResource(regexFile);
		String regexs;
		try {
			regexs = IOUtils.toString(resource.getInputStream());
			for(String regex : regexs.split(IConstantsUtil.LINE_SEPARATOR)){
				if (!StringUtils.isEmpty(regex)){
						StringBuffer stringBuf = new StringBuffer();
						stringBuf.append(prefix)
							.append(CustomStringUtils.removeBackLine(regex))
							.append(allContaining);
						precompiledRegex.add(Pattern.compile(stringBuf.toString(),Pattern.CASE_INSENSITIVE));
				}
			}
		} catch (IOException e) {
			StringBuffer excMessage = new StringBuffer()
				.append("regex configuration file named ")
				.append(regexFile)
				.append(" was not successfully loaded");
			logService.error(this.getClass(), excMessage.toString());
			// on jete une erreur car il s'agit d'un probleme critique
			throw new IllegalStateException(excMessage.toString());
		}
		return precompiledRegex;
	}
	
	/**
	 * Jete une exception non vérifié et log l'erreur
	 * 
	 * @param configFileName
	 */
	private void handleCriticalErrorLoadingConfigurationFile(String configFileName){
		StringBuffer excMessage = new StringBuffer()
			.append("regex configuration file named ")
			.append(configFileName)
			.append(" was not successfully loaded");
		logService.error(this.getClass(), excMessage.toString());
		// on jete une erreur car il s'agit d'un probleme critique
		throw new IllegalStateException(excMessage.toString());
	}
}
